#pragma once

namespace SD_Tools
{
	enum SIC_STAT
	{
		SICS_FAIL = -1,
		SICS_OK = 0,
		SICS_CONNECT_FAIL,
		SICS_SEND_FAIL,
		SICS_SOCKET_CREATION_FAIL,
		SICS_NO_CHANNEL_SELECTED
	};

	enum SIC_TYPE
	{
		SICT_ERRORMESSAGE = -1,
		SICT_MESSAGE = 0,
		SICT_PRIVATEMESSAGE,
		SICT_WHISPERMESSAGE,
		SICT_INFOMESSAGE,
		SICT_BADINFOMESSAGE
	};
	
	typedef void(*MessageCallback_t)(SIC_TYPE, char* Message);

	class SimpleIrcClient
	{
	private:
		MessageCallback_t MessageCallback;
		char AllroundBuffer[1024];
		SOCKET MySocket;

		bool ConnectionEstablished;
		char* CurrentChannel;
		char* CurrentIp;
		short CurrentPort;
		char* CurrentNick;
		char* CurrentName;
	public:
	private:
		bool ParseCommand(char* Out, int MaxLen);
	public:
		SimpleIrcClient();
		~SimpleIrcClient();
		SIC_STAT Init();
		SIC_STAT Connect(char* Ip, short Port = 6667);
		SIC_STAT ConnectDns(char* Dns, short Port = 6667);
		SIC_STAT SetNick(char* Nick);
		SIC_STAT SetRealName(char* Name);
		SIC_STAT Join(char* ChannelName);
		SIC_STAT Say(char* ChannelName, char* Text);
		SIC_STAT Say(char* Text);
		SIC_STAT Part(char* ChannelName);
		SIC_STAT SetMessageCallback(MessageCallback_t NewCallback);
		bool IsConnectionEstablished();

		SIC_STAT Receive();
	};

	SimpleIrcClient::SimpleIrcClient()
	{
		ConnectionEstablished = false;
		MySocket = NULL;
		CurrentChannel = NULL;
		CurrentIp = NULL;
		CurrentPort = 6667;
		CurrentNick = new char[64];
		strcpy(CurrentNick, "SilverIRC");
		CurrentName = new char[64];
		strcpy(CurrentName, "SilverDeath");

		MessageCallback = (MessageCallback_t) NULL;
	}

	SimpleIrcClient::~SimpleIrcClient()
	{
		if (CurrentChannel)
		{
			delete[] CurrentChannel;
		}
		if (CurrentIp)
		{
			delete[] CurrentIp;
		}
		delete[] CurrentNick;
		delete[] CurrentName;
		if (MySocket)
		{
			closesocket(MySocket);
		}
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Init()
	{
		MySocket = socket(AF_INET, SOCK_STREAM, 0);
		if (MySocket == INVALID_SOCKET)
		{
			return SICS_SOCKET_CREATION_FAIL;
		}
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Connect( char* Ip, short Port /*= 6667*/ )
	{
		SOCKADDR_IN MyAddr = {0};
		ConnectionEstablished = false;

		if (CurrentIp)
		{
			delete[] CurrentIp;
		}
		CurrentIp = new char[strlen(Ip)+1];
		strcpy(CurrentIp, Ip);

		MyAddr.sin_family = AF_INET;
		MyAddr.sin_port = htons(Port);
		MyAddr.sin_addr.s_addr = inet_addr(CurrentIp);

		if (connect(MySocket, (SOCKADDR*) &MyAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)
		{
			return SICS_CONNECT_FAIL;
		}

		sprintf(AllroundBuffer, "NICK %s\r\n", CurrentNick);
		if (send(MySocket, AllroundBuffer, strlen(AllroundBuffer), 0) == SOCKET_ERROR)
		{
			return SICS_SEND_FAIL;
		}

		sprintf(AllroundBuffer, "USER %s \"\" \"\" :%s\r\n", CurrentNick, CurrentName);
		if (send(MySocket, AllroundBuffer, strlen(AllroundBuffer), 0) == SOCKET_ERROR)
		{
			return SICS_SEND_FAIL;
		}

		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::ConnectDns( char* Dns, short Port /*= 6667*/ )
	{
		// Todo...
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::SetNick( char* Nick )
	{
		delete[] CurrentNick;
		CurrentNick = new char[strlen(Nick)+1];
		strcpy(CurrentNick, Nick);
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::SetRealName( char* Name )
	{
		delete[] CurrentName;
		CurrentName = new char[strlen(Name)+1];
		strcpy(CurrentName, Name);
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Join( char* ChannelName )
	{
		if (CurrentChannel)
		{
			delete[] CurrentChannel;
		}
		CurrentChannel = new char[strlen(ChannelName)+1];
		strcpy(CurrentChannel, ChannelName);

		sprintf(AllroundBuffer, "JOIN %s\r\n", CurrentChannel);
		if (send(MySocket, AllroundBuffer, strlen(AllroundBuffer), 0) == SOCKET_ERROR)
		{
			return SICS_SEND_FAIL;
		}
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Say( char* ChannelName, char* Text )
	{
		sprintf(AllroundBuffer, "PRIVMSG %s %s\r\n", ChannelName, Text);
		if (send(MySocket, AllroundBuffer, strlen(AllroundBuffer), 0) == SOCKET_ERROR)
		{
			return SICS_SEND_FAIL;
		}
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Say( char* Text )
	{
		if (!CurrentChannel)
		{
			return SICS_NO_CHANNEL_SELECTED;
		}

		sprintf(AllroundBuffer, "PRIVMSG %s :%s\r\n", CurrentChannel, Text);
		if (send(MySocket, AllroundBuffer, strlen(AllroundBuffer), 0) == SOCKET_ERROR)
		{
			return SICS_SEND_FAIL;
		}
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Part( char* ChannelName )
	{
		if (strcmp(ChannelName, CurrentChannel) == 0)
		{
			delete[] CurrentChannel;
			CurrentChannel = NULL;
		}

		sprintf(AllroundBuffer, "PART %s %s\r\n", ChannelName, "Leaves...");
		if (send(MySocket, AllroundBuffer, strlen(AllroundBuffer), 0) == SOCKET_ERROR)
		{
			return SICS_SEND_FAIL;
		}
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::SetMessageCallback( MessageCallback_t NewCallback )
	{
		MessageCallback = (MessageCallback_t) NewCallback;
		return SICS_OK;
	}

	SD_Tools::SIC_STAT SimpleIrcClient::Receive()
	{
		char OutBuffer[512];
		char SendBuffer[512];
		char SuffixBuffer[512];
		while (true)
		{
			if (ParseCommand(OutBuffer, 512))
			{
				printf("%s\n", OutBuffer);
				if (sscanf(OutBuffer, "PING :%s", SuffixBuffer) > 0)
				{
					ConnectionEstablished = true;
					sprintf(SendBuffer, "PONG :%s\r\n", SuffixBuffer);
					if (send(MySocket, SendBuffer, strlen(SendBuffer), 0) == SOCKET_ERROR)
					{
						// Todo...
					}
				}
			}
		}
	}

	bool SimpleIrcClient::ParseCommand( char* Out, int MaxLen )
	{
		char SingleChar;
		int CurrentIndex = 0;
		while (CurrentIndex < MaxLen-1)
		{
			if (recv(MySocket, &SingleChar, 1, 0) == SOCKET_ERROR)
			{
				return false;
			}
			if (SingleChar == '\n')
			{
				Out[CurrentIndex] = SingleChar;
				Out[CurrentIndex+1] = '\0';
				return true;
			}
			else
			{
				Out[CurrentIndex] = SingleChar;
				CurrentIndex++;
			}
		}
		return false;
	}

	bool SimpleIrcClient::IsConnectionEstablished()
	{
		return ConnectionEstablished;
	}

}







